package com.hitqz.robot.driver.service;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hitqz.robot.api.common.dto.robot.RobotOnlineEventDto;
import com.hitqz.robot.driver.dto.robot.MessageRobotDto;
import com.hitqz.robot.driver.netty.client.RobotControllerClient;
import com.hitqz.robot.driver.schedule.CheckAliveSchedule;
import com.hitqz.robot.driver.util.RobotStatusUtil;
import com.hitqz.robot.driver.util.SeqUtil;
import com.hitqz.robot.driver.util.SourceEnum;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author xupkun
 * @date 2023/8/7
 */
@Slf4j
public class NettyChannelService {

    protected static Map<String, ChannelId> currentTable  = Maps.newConcurrentMap();

    private static ChannelGroup GlobalGroup=new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);


    public static List<String> GlobalId = Lists.newArrayList();

    public static String ipAddress = "";

    public static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10,20,
            1L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(20),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.DiscardPolicy());

    /**
     *
     * @param channelId
     * @return
     */
    public static Channel findByChannelId(ChannelId channelId) {
        return GlobalGroup.find(channelId);
    }

    /**
     * @param source
     * @param id
     * @param ctx
     */
    public static synchronized void addChannel(String source, ChannelHandlerContext ctx) {
        if(findByChannelId(ctx.channel().id()) == null) {
            GlobalGroup.add(ctx.channel());
            currentTable.put(source, ctx.channel().id());
            log.info("add channel {}, ---- {}--- {} ", source, ctx.channel().remoteAddress().toString(), currentTable.size());
            GlobalId.add(source);
        } else {
            log.info("channel already exists:{}, {}", source, ctx.channel().remoteAddress().toString());
        }
    }


    public static boolean checkHasConnection(String source) {
        return  currentTable.get(source) == null;
    }


    /**
     * 发送消息
     * @param source
     * @param id
     * @param bytes
     */
    public static void sendMessage(String source, Short sn, Integer cmd, byte[] bytes) {
        ChannelId channelId = currentTable.get(source);
        if(channelId == null) {
            //log.info("channelId not found:{}", source);
            return;
        }
        Channel channel = GlobalGroup.find(channelId);
        if (channel != null) {
            channel.writeAndFlush(new MessageRobotDto(sn, cmd.shortValue(), bytes == null ? null : new String(bytes, StandardCharsets.UTF_8)
            ));
        }

    }

    /**
     *
     * @param sourceEnum
     * @param id
     * @param ctx
     */
    public static void removeChannel(SourceEnum sourceEnum, ChannelHandlerContext ctx) {
        Channel  channel = findByChannelId(ctx.channel().id());
        log.info("移除channel {}", ctx.channel().remoteAddress().toString());
        if (channel != null) {
            GlobalGroup.remove(channel);
            currentTable.remove(sourceEnum.getSource());
        }
        //NettyChannelService(sourceEnum);
    }



    /**
     *
     * @param source
     * @param id
     * @return
     */
    public static ChannelId findBySourceAndId(String source) {
        return currentTable.get(source);
    }


    /**
     * 获取定位状态
     * @return
     */
    public static int getRobotStatusReLoc() {
        Lock lock = RobotStatusUtil.ROBOT_STATUS_RELOC_LOCK;
        if (lock ==  null) {
            lock = new ReentrantLock();
            RobotStatusUtil.ROBOT_STATUS_RELOC_LOCK = lock;
        }
        try{
            NettyChannelService.sendMessage(
                    SourceEnum.ROBOT_STATUS.getSource(), SeqUtil.nextId(),
                    0x03FD, null
            );
            lock.wait(5*1000);
        } catch (Exception e) {
        }
        return RobotStatusUtil.ROBOT_RELOC_STATUS;
    }

    public static void confirmReLocStatus() {
        NettyChannelService.sendMessage(
                SourceEnum.ROBOT_CONTROLLER.getSource(), SeqUtil.nextId(),
                2003, null
        );
    }

    public static void executeReLoc() {

    }

    /**
     * 上线逻辑
     * @param times
     */
    public static void changeRobotOnline(int times){
        log.info("成功连接到机器人控制模块次数:{}", times);
        try {
            TimeUnit.SECONDS.sleep(10);
        }catch (Exception e) {

        }
        if (times > 10) {
            log.info("over max tiems and reporting error");
            return;
        }
        RobotOnlineEventDto dto = new RobotOnlineEventDto();
        dto.setOnlineStatus(1);
        SpringUtil.getApplicationContext().publishEvent(dto);
        int relocStatus=  NettyChannelService.getRobotStatusReLoc();
        log.info("定位状态:" + relocStatus);
        if (relocStatus == -1 || relocStatus == 0 || relocStatus == 2) {
            log.info("获取定位状态失败执行重定位{}", relocStatus);
            if (relocStatus !=2) {
                NettyChannelService.executeReLoc();
            }
            try {
                TimeUnit.SECONDS.sleep(3);
                NettyChannelService.changeRobotOnline(times +1);
            } catch (Exception e) {
            }
        } else if (relocStatus  == 1) {
            // 定位正确
            // 确定定位
            log.info("机器人可以正常上线");
            RobotOnlineEventDto robotChangeOnlineStatusDto = new RobotOnlineEventDto();
            robotChangeOnlineStatusDto.setOnlineStatus(1);
            SpringUtil.getApplicationContext().publishEvent(robotChangeOnlineStatusDto);
        } else if (relocStatus == 3) {
            // 定位完成
            NettyChannelService.confirmReLocStatus();
            try {
                TimeUnit.SECONDS.sleep(2);
                NettyChannelService.changeRobotOnline(0);
                // 同步点位信息

            }  catch (Exception e) {
            }
        }
    }


    public static void connect(String ip, SourceEnum sourceEnum) {
        try {

            if (StrUtil.isEmpty(ip) && StrUtil.isEmpty(ipAddress)) {
                log.info("无法连接至机器人");
                return;
            }
            CheckAliveSchedule.ip = ip;
            ipAddress = ip;
            String finalIp = ipAddress;
            if (StrUtil.isEmpty(ip)) {
                ip = ipAddress;
            }
            if (sourceEnum == null || sourceEnum == SourceEnum.ROBOT_MESSAGE_PUSH) {
                threadPool.execute(() -> {
                    try {
                        RobotControllerClient.connect(finalIp,19301, SourceEnum.ROBOT_MESSAGE_PUSH);
                    } catch (Exception e) {
                        log.info("异常：19301"  + e.getMessage());
                        throw new RuntimeException(e);
                    }
                });
            }

            if (sourceEnum == null || sourceEnum == SourceEnum.ROBOT_NAV) {
                threadPool.execute(() -> {
                    try {

                        RobotControllerClient.connect(finalIp, 19206, SourceEnum.ROBOT_NAV);
                    } catch (Exception e) {
                        log.info("异常：19206" + e.getMessage());
                        throw new RuntimeException(e);
                    }
                });
            }
            if (sourceEnum == null || sourceEnum == SourceEnum.ROBOT_OTHER) {
                threadPool.execute(() -> {
                    try {
                        RobotControllerClient.connect(finalIp, 19210, SourceEnum.ROBOT_OTHER);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                });
            }
            if (sourceEnum == null || sourceEnum == SourceEnum.ROBOT_STATUS) {
                threadPool.execute(() -> {
                    try {
                        RobotControllerClient.connect(finalIp, 19204, SourceEnum.ROBOT_STATUS);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                });
            }


            if (sourceEnum == null || sourceEnum == SourceEnum.ROBOT_CONTROLLER) {
                threadPool.execute(() -> {
                    try {
                        RobotControllerClient.connect(finalIp, 19205, SourceEnum.ROBOT_CONTROLLER);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                });
            }

            if (sourceEnum == null || sourceEnum == SourceEnum.ROBOT_CONFIG) {
                threadPool.execute(() -> {
                    try {
                        RobotControllerClient.connect(finalIp, 19207, SourceEnum.ROBOT_CONFIG);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                });
            }

        }catch (Exception e) {
            log.info("连接发生异常: " + e.getMessage());
            threadPool.execute(()-> {
                NettyChannelService.connect(ipAddress, null);
            });
        }
    }

}
